package com.workcell.workdata.xthings.service.impl.mqtt;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.workcell.workdata.xthings.config.DynamicTableUtils;
import com.workcell.workdata.xthings.config.RequestDataHelper;
import com.workcell.workdata.xthings.dto.CodeChainnumVo;
import com.workcell.workdata.xthings.dto.ReturnDataVO;
import com.workcell.workdata.xthings.entity.mqtt.BeltScalesData;
import com.workcell.workdata.xthings.entity.mqtt.FlowMeterData;
import com.workcell.workdata.xthings.entity.mqtt.PowerMeterData;
import com.workcell.workdata.xthings.mapper.mqtt.PowerMeterDataMapper;
import com.workcell.workdata.xthings.service.BeltScalesRealDataService;
import com.workcell.workdata.xthings.service.PowerMeterRealDataService;
import com.workcell.workdata.xthings.service.factory.HardwareRealDataService;
import com.workcell.workdata.xthings.utils.DateTimeUtil;
import com.workcell.workdata.xthings.utils.ObjectUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.workcell.workdata.xthings.config.DynamicTableUtils.POWER_METER_TABLE_NAME;

/**
 * @Author 廖博文
 * @Date 2022/12/5 14:16
 * @Description 功率计真实数据
 */
@Service
@DS("slave_mqtt")
@RequiredArgsConstructor
public class PowerMeterRealDataServiceImpl extends ServiceImpl<PowerMeterDataMapper, PowerMeterData> implements HardwareRealDataService, PowerMeterRealDataService {

	private final RedisTemplate redisTemplate;
	private final String baseCodeBaseRedisKey = "DataQrCode_PowerMeter_DataHourLineChartInfo_%s_%s";
	private final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
	/** 流量计固定1分钟内真实数据*/
	private final Integer dataRealTime = 1;

	/**
	 * 获取硬件最近的一次数据
	 * @param deviceNo 设备编号
	 * @param time
	 * @return
	 */
	@Override
	public Object getLatelyDataInfo(String deviceNo, String manufacturerCode, Integer time) {
		RequestDataHelper.setRequestData(new HashMap<String, Object>() {{
			put("t_power_meter_data", DynamicTableUtils.getPowerMeterTodayTableName());
		}});
		return this.baseMapper.getLatelyDataInfoByDeviceNo(deviceNo, manufacturerCode, dataRealTime);
	}

	/**
	 * 获取硬件最近六个小时的数据：折线图
	 * @param deviceNo
	 * @return
	 */
	@Override
	public Object getDataHourLineChartInfo(String deviceNo, String manufacturerCode) {
		Map<String, PowerMeterData> dbPowerMeterDataNew = new LinkedHashMap<>(6);
		// 获取当前小时时间
		RequestDataHelper.setRequestData(new HashMap<String, Object>() {{
			put("t_power_meter_data", DynamicTableUtils.getPowerMeterTodayTableName());
		}});
		Date nowDayTime = DateTimeUtil.addDayAndFormatSM(0);
		Date nowHourTime = DateTimeUtil.addHourAndFormatSM(0);
		Date nowHourTimeAddOne = DateTimeUtil.addHourAndFormatSM(1);
		checkRedisSearchDbDataChar(dbPowerMeterDataNew, deviceNo, manufacturerCode, nowHourTime, nowHourTimeAddOne);
		// 处理一个小时前的数据
		Date nowHourTimeSub1 = DateTimeUtil.addHourAndFormatSM(-1);
		checkSourceTable(nowHourTimeSub1,nowDayTime);
		checkRedisSearchDbDataChar(dbPowerMeterDataNew, deviceNo, manufacturerCode, nowHourTimeSub1, nowHourTime);
		// 处理两个小时前的数据
		Date nowHourTimeSub2 = DateTimeUtil.addHourAndFormatSM(-2);
		checkSourceTable(nowHourTimeSub2,nowDayTime);
		checkRedisSearchDbDataChar(dbPowerMeterDataNew, deviceNo, manufacturerCode, nowHourTimeSub2, nowHourTimeSub1);
		// 处理三个小时前的数据
		Date nowHourTimeSub3 = DateTimeUtil.addHourAndFormatSM(-3);
		checkSourceTable(nowHourTimeSub3,nowDayTime);
		checkRedisSearchDbDataChar(dbPowerMeterDataNew, deviceNo, manufacturerCode, nowHourTimeSub3, nowHourTimeSub2);
		// 处理四个小时前的数据
		Date nowHourTimeSub4 = DateTimeUtil.addHourAndFormatSM(-4);
		checkSourceTable(nowHourTimeSub4,nowDayTime);
		checkRedisSearchDbDataChar(dbPowerMeterDataNew, deviceNo, manufacturerCode, nowHourTimeSub4, nowHourTimeSub3);
		// 处理五个小时前的数据
		Date nowHourTimeSub5 = DateTimeUtil.addHourAndFormatSM(-5);
		checkSourceTable(nowHourTimeSub5,nowDayTime);
		checkRedisSearchDbDataChar(dbPowerMeterDataNew, deviceNo, manufacturerCode, nowHourTimeSub5, nowHourTimeSub4);
		// 组装数据
		JSONArray typeArray = new JSONArray();
		JSONArray timeArray = new JSONArray();
		JSONArray informationArray = new JSONArray();

		// 逆序遍历
		ListIterator<Map.Entry<String, PowerMeterData>> i =
				new ArrayList<>(dbPowerMeterDataNew.entrySet()).listIterator(dbPowerMeterDataNew.size());

		Map<String,JSONArray> jsonObjectMap = new HashMap<>();
		while(i.hasPrevious()) {
			Map.Entry<String, PowerMeterData> entry = i.previous();
			timeArray.add(entry.getKey());
			//转换实体类为map
			List<Map<String, Object>> maps = ObjectUtils.setConditionToListMap(entry.getValue());
			maps.stream().forEach(m -> {
				String key = (String) m.get("key");
				Object value = m.get("value");
				//将所有同字段数据进行存储
				if (jsonObjectMap.containsKey(key)){
					JSONArray objects = jsonObjectMap.get(key);
					objects.add(value);
				}else {
					JSONArray objects = new JSONArray();
					objects.add(value);
					jsonObjectMap.put(key,objects);
				}

			});
		}
		//组装成前端需要的数据
		jsonObjectMap.keySet().forEach(m -> {
			typeArray.add(m);
			JSONObject infoObj = new JSONObject();
			infoObj.put("name",m);
			infoObj.put("data",jsonObjectMap.get(m));
			informationArray.add(infoObj);
		});
		typeArray.sort(Comparator.comparing(type -> type.toString()));

		JSONObject jsonObject = new JSONObject();
		jsonObject.put("typeArray",typeArray);
		jsonObject.put("timeArray",timeArray);
		jsonObject.put("informationArray",informationArray);
		return jsonObject;
	}

	/**
	 * 判断redis存在当前时间段的缓存，否则查询库存储
	 * @param dbPowerMeterDataNew 查询的信息存储
	 * @param deviceNo 设备编号
	 * @param nowHourTime 开始时间
	 * @param nowHourTimeAddOne 结束时间
	 */
	public void checkRedisSearchDbDataChar(Map<String, PowerMeterData> dbPowerMeterDataNew, String deviceNo, String manufacturerCode, Date nowHourTime, Date nowHourTimeAddOne){
		Object nowHourTimeDate = redisTemplate.opsForValue().get(String.format(baseCodeBaseRedisKey,deviceNo,nowHourTime));
		if (ObjectUtil.isEmpty(nowHourTimeDate)){
			nowHourTimeDate = this.baseMapper.getDataHourLineChartInfo(deviceNo, manufacturerCode, nowHourTime, nowHourTimeAddOne);
			if (ObjectUtil.isNotEmpty(nowHourTimeDate)){
				redisTemplate.opsForValue().set(String.format(baseCodeBaseRedisKey,deviceNo,nowHourTime),nowHourTimeDate,1, TimeUnit.DAYS);
			}
		}
		dbPowerMeterDataNew.put(sdf.format(nowHourTime),ObjectUtil.isEmpty(nowHourTimeDate)?new PowerMeterData():(PowerMeterData)nowHourTimeDate);
	}

	/**
	 * 判断是否需要切换库
	 * @param nowHourTime 当前判断的日期
	 * @param nowDayTime 当前的天数日期
	 */
	public void checkSourceTable(Date nowHourTime, Date nowDayTime){
		if (nowHourTime.compareTo(nowDayTime) <= 0){
			RequestDataHelper.setRequestData(new HashMap<String, Object>() {{
				put("t_power_meter_data", DynamicTableUtils.getPowerMeterYesterdayTableName());
			}});
		}
	}

	/**
	 * 查询采集数据
	 * @param selectChainNumDataVo
	 * @return
	 */
	@Override
	public Page<PowerMeterData> getHardwareDataInfo(CodeChainnumVo.SelectChainNumDataVo selectChainNumDataVo) {
		LambdaQueryWrapper<PowerMeterData> powerMeterDataCondition = new QueryWrapper<PowerMeterData>().lambda()
				.eq(ObjectUtil.isNotEmpty(selectChainNumDataVo.getDeviceNo()), PowerMeterData::getSlaveDeviceNo, selectChainNumDataVo.getDeviceNo())
				.eq(ObjectUtil.isNotEmpty(selectChainNumDataVo.getManufacturerCode()), PowerMeterData::getManufacturerCode, selectChainNumDataVo.getManufacturerCode())
				.and(ObjectUtil.isNotEmpty(selectChainNumDataVo.getStartTime()) || ObjectUtil.isNotEmpty(selectChainNumDataVo.getEndTime())
						, i -> i.between(PowerMeterData::getCreateTime, selectChainNumDataVo.getStartTime(), selectChainNumDataVo.getEndTime()));
		Page<PowerMeterData> page = new Page<>(selectChainNumDataVo.getPageNum(), selectChainNumDataVo.getPageSize());
		RequestDataHelper.setRequestData(new HashMap<String, Object>() {{
			put("t_power_meter_data", POWER_METER_TABLE_NAME + "_" + DateUtil.format(selectChainNumDataVo.getStartTime(), DatePattern.PURE_DATE_PATTERN));
		}});
		return baseMapper.selectPage(page,powerMeterDataCondition);
	}

	@Override
	public PowerMeterData returnPowerMeterData(ReturnDataVO returnDataVO) {
		Assert.isTrue(ObjectUtil.isNotEmpty(returnDataVO.getDeviceNo()),"设备编码不能为空");
		String newCreate = returnDataVO.getStartTime().toString();
		PowerMeterData dataHourInfo = this.baseMapper.getDataByStartTime(returnDataVO.getTableName(),returnDataVO.getManufacturerCode(),returnDataVO.getDeviceNo(), newCreate);
		return dataHourInfo;
	}

	@Override
	public Object getRealData(String deviceNo) {
		LocalDateTime endTime = LocalDateTimeUtil.now();
		LocalDateTime startTime = LocalDateTimeUtil.offset(endTime, -1, ChronoUnit.MINUTES);
		LambdaQueryWrapper<PowerMeterData> lambdaQueryWrapper = Wrappers.lambdaQuery(PowerMeterData.class);
		lambdaQueryWrapper.eq(PowerMeterData::getSlaveDeviceNo,deviceNo);
		lambdaQueryWrapper.between(PowerMeterData::getCreateTime,startTime,endTime);
		lambdaQueryWrapper.orderByDesc(PowerMeterData::getCreateTime);
		return this.getOne(lambdaQueryWrapper);
	}
}
